# switch_test = {'00':
#                    {'000': ['c.addi4spn'],
#                     '010': ['c.lw'],
#                     '011': ['c.flw'],
#                     '001': ['c.fld'],
#                     '110': ['c.sw'],
#                     '111': ['c.fsw'],
#                     '101': ['c.fsd']
#                     },
#                '01':
#                    {'000': ['c.addi', 'c.nop'],
#                     '011': ['c.addi16sp', 'c.lui'],
#                     '010': ['c.li'],
#                     '100': ['c.and', 'c.or', 'c.xor', 'c.sub', 'c.srli', 'c.srai', 'c.andi'],
#                     '110': ['c.beqz'],
#                     '111': ['c.bnez'],
#                     '101': ['c.j'],
#                     '001': ['c.jal']
#                     },
#                '10':
#                    {'100': ['c.add', 'c.mv', 'c.jr', 'c.jalr', 'c.ebreak'],
#                     '010': ['c.lwsp'],
#                     '011': ['c.flwsp'],
#                     '001': ['c.fldsp'],
#                     '000': ['c.slli'],
#                     '110': ['c.swsp'],
#                     '111': ['c.fswsp'],
#                     '101': ['c.fsdsp']
#                     },
#                '11':
#                    {}
#                }


def c_addi4spn(instruction, registers, memory, pc, command):
    # "{:03b}{:08b}{:03b}{:02b}".format(data_list[3], data_list[2], data_list[1], data_list[0])
    func3 = instruction[0:3]
    imm = int(instruction[3:11], 2)
    rd_1 = int(instruction[11:14], 2)
    op = instruction[14:]
    rd = rd_1 + 8
    command = 'c.addi4spn'
    registers[rd] = imm*4 + registers[2]
    return instruction, registers, memory, pc, command


def c_lw(instruction, registers, memory, pc, command):
    funct3 = instruction[0:3]
    imm_low = instruction[3:6]
    rs1_1 = instruction[6:9]
    imm_high = instruction[9:11]
    rd_1 = instruction[11:14]
    op = instruction[14:]

    rs1 = int(rs1_1, 2) + 8
    rd = int(rd_1, 2) + 8
    imm = imm_high + imm_low
    imm = int(imm, 2)

    command = 'c.lw'
    registers[rd] = memory[str(registers[rs1] + imm)]
    return instruction, registers, memory, pc, command


def c_flw(instruction, registers, memory, pc, command):
    pass


def c_fld(instruction, registers, memory, pc, command):
    pass


def c_sw(instruction, registers, memory, pc, command):
    funct3 = instruction[0:3]
    imm_low = instruction[3:6]
    rs1_1 = int(instruction[6:9], 2)
    imm_high = instruction[9:11]
    rs2_1 = int(instruction[11:14], 2)
    op = instruction[14:]

    imm = imm_high + imm_low
    imm = int(imm, 2)
    rs1 = rs1_1 + 8
    rs2 = rs2_1 + 8

    command = 'c.sw'
    memory[str(registers[rs1] + imm)] = registers[rs2]
    return instruction, registers, memory, pc, command


def c_fsw(instruction, registers, memory, pc, command):
    pass


def c_fsd(instruction, registers, memory, pc, command):
    pass


def c_addi_nop(instruction, registers, memory, pc, command):
    funct3 = instruction[0:3]
    imm_high = instruction[3]
    rd = instruction[4:9]
    imm_low = instruction[9:14]
    op = instruction[14:]

    imm = imm_high + imm_low
    rd = int(rd, 2)
    imm = int(imm, 2)

    if rd == 0:  # c.nop
        command = 'c.nop'
    else:  # c.addi
        command = 'c.addi'
        registers[rd] = registers[rd] + imm
    return instruction, registers, memory, pc, command


def c_addi16sp_lui(instruction, registers, memory, pc, command):
    funct3 = instruction[0:3]
    imm_high = instruction[3]
    rd = instruction[4:9]
    imm_low = instruction[9:14]
    op = instruction[14:]

    imm = imm_high + imm_low
    rd = int(rd, 2)
    imm = int(imm, 2)

    if rd == 2:  # c.addi16sp
        command = 'c.addi16sp'
        registers[2] = registers[2] + imm * 16
    else:  # c.lui
        command = 'c.lui'
        registers[rd] = imm << 12
    return instruction, registers, memory, pc, command


def c_li(instruction, registers, memory, pc, command):
    funct3 = instruction[0:3]
    imm_high = instruction[3]
    rd = instruction[4:9]
    imm_low = instruction[9:14]
    op = instruction[14:]

    imm = imm_high + imm_low
    rd = int(rd, 2)
    imm = int(imm, 2)

    command = 'c.li'
    registers[rd] = imm
    return instruction, registers, memory, pc, command


def c_and_or_xor_sub_srli_srai_andi(instruction, registers, memory, pc, command):
    func6 = instruction[0:6]
    if func6 == '100011': # c.sub, c.xor, c.or, c.and
        rd_1 = int(instruction[6:9], 2)
        func2_alu = instruction[9:11]
        rs2_1 = int(instruction[11:14], 2)
        op = instruction[14:]
        rd_alu = rd_1 + 8
        rs_alu = rs2_1 + 8
        if func2_alu == '00':  # c.sub
            command = 'c.sub'
            registers[rd_alu] = registers[rd_alu] - registers[rs_alu]
        elif func2_alu == '01':  # c.xor
            command = 'c.xor'
            registers[rd_alu] = registers[rd_alu] ^ registers[rs_alu]
        elif func2_alu == '10':  # c.or
            command = 'c.or'
            registers[rd_alu] = registers[rd_alu] | registers[rs_alu]
        elif func2_alu == '11':  # c.and
            command = 'c.and'
            registers[rd_alu] = registers[rd_alu] & registers[rs_alu]
    else:  # c.srli, c.srai, c.andi
        func3 = instruction[0:3]
        imm_high = instruction[3]
        func2 = instruction[4:6]
        rd_1 = int(instruction[6:9], 2)
        rd = rd_1 + 8
        imm_low = instruction[9:14]
        imm = imm_high + imm_low
        imm = int(imm, 2)
        op = instruction[14:]
        if func2 == '00':  # c.srli
            command = 'c.srli'
            registers[rd] = registers[rd] >> imm
        elif func2 == '01':  # c.srai
            command = 'c.srai'
            registers[rd] = registers[rd] << imm
        elif func2 == '10':  # c.andi
            command = 'c.andi'
            registers[rd] = registers[rd] & imm
    return instruction, registers, memory, pc, command


def c_beqz(instruction, registers, memory, pc, command):
    func3 = instruction[0:3]
    imm_low = instruction[3:6]
    rs_1 = int(instruction[6:9], 2)
    imm_high = instruction[9:14]
    op = instruction[14:]

    imm = imm_high + imm_low
    imm = int(imm, 2)
    rs = rs_1 + 8

    command = 'c.beqz'
    if registers[rs] == 0:
        pc = pc + imm
    return instruction, registers, memory, pc, command


def c_bnez(instruction, registers, memory, pc, command):
    func3 = instruction[0:3]
    imm_low = instruction[3:6]
    rs_1 = int(instruction[6:9], 2)
    imm_high = instruction[9:14]
    op = instruction[14:]

    imm = imm_high + imm_low
    imm = int(imm, 2)
    rs = rs_1 + 8

    command = 'c.bnez'
    if registers[rs] != 0:
        pc = pc + imm
    return instruction, registers, memory, pc, command


def c_j(instruction, registers, memory, pc, command):
    pass


def c_jal(instruction, registers, memory, pc, command):
    funct3 = instruction[0:3]
    imm = instruction[3:14]
    op = instruction[14:]

    imm = int(imm, 2)

    registers[1] = pc + 2
    pc = imm
    command = 'c.jal'
    return instruction, registers, memory, pc, command



def c_add_mv_jr_jalr_ebreak(instruction, registers, memory, pc, command):
    funct4 = instruction[0:4]
    rd = instruction[4:9]
    rs2 = instruction[9:14]
    op = instruction[14:]

    rd = int(rd, 2)
    rs2 = int(rs2, 2)

    if rs2 == 0 & rd == 0:
        command = 'c.ebreak'
        return instruction, registers, memory, pc, command

    if rs2 == 0:  # c.jr or c.jalr
        if funct4 == '1000':  # c.jr
            command = 'c.jr'
            pc = registers[rd]
        elif funct4 == '1001':  # c.jalr
            command = 'c.jalr'
            registers[1] = pc + 2
            pc = registers[rd]
    else: # c.mc or c.add
        if funct4 == '1000': # c.mv
            command = 'c.mv'
            registers[rd] = registers[rs2]
        elif funct4 == '1001': # c.add
            command = 'c.add'
            registers[rd] = registers[rs2] + registers[rd]
    return instruction, registers, memory, pc, command


def c_lwsp(instruction, registers, memory, pc, command):
    funct3 = instruction[0:3]
    imm_high = instruction[3]
    rd = instruction[4:9]
    imm_low = instruction[9:14]
    op = instruction[14:]

    imm = imm_high + imm_low
    rd = int(rd, 2)
    imm = int(imm, 2)

    command = 'c.lwsp'
    registers[rd] = memory[str(registers[2] + imm)]
    return instruction, registers, memory, pc, command


def c_flwsp(instruction, registers, memory, pc, command):
    pass


def c_fldsp(instruction, registers, memory, pc, command):
    pass


def c_slli(instruction, registers, memory, pc, command):
    pass


def c_swsp(instruction, registers, memory, pc, command):
    funct3 = instruction[0:3]
    imm = instruction[3:9]
    rs2 = instruction[9:14]
    op = instruction[14:]

    rs2 = int(rs2, 2)
    imm = int(imm, 2)

    command = 'c.swsp'
    memory[str(registers[2] + imm)] = registers[rs2]
    return instruction, registers, memory, pc, command


def c_fswsp(instruction, registers, memory, pc, command):
    pass


def c_fsdsp(instruction, registers, memory, pc, command):
    pass


switch = {'00':
              {'000': c_addi4spn, '010': c_lw, '011': c_flw, '001': c_fld, '110': c_sw, '111': c_fsw, '101': c_fsd},
          '01':
              {'000': c_addi_nop, '011': c_addi16sp_lui, '010': c_li, '100': c_and_or_xor_sub_srli_srai_andi,
               '110': c_beqz, '111': c_bnez, '101': c_j, '001': c_jal},
          '10':
              {'100': c_add_mv_jr_jalr_ebreak, '010': c_lwsp, '011': c_flwsp, '001': c_fldsp, '000': c_slli,
               '110': c_swsp, '111': c_fswsp, '101': c_fsdsp},
          '11': {}
          }
